package com.gf.framework.redis.util;

import com.gf.framework.common.exception.BizException;
import com.gf.framework.common.utils.SpringContextUtil;
import org.springframework.data.redis.core.RedisTemplate;

import java.time.Duration;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * @author gaofei
 * @date 2022/6/22 21:36
 */
public class RedisMap<V> implements Map<String, V> {
    private RedisTemplate<String, V> redisTemplate = SpringContextUtil.getBean("redisTemplate", RedisTemplate.class);
    private Long timeOut;

    public static <V> RedisMap<V> create(String mapName, Long timeOut) {
        return new RedisMap<>(mapName, timeOut);
    }

    private String mapName;
    private RedisMap(String mapName, Long timeOut) {
        this.mapName = mapName.concat(":");
        this.timeOut = timeOut;
    }

    private String getRedisKey(Object key) {
        return mapName.concat(key.toString());
    }

    @Override
    public int size() {
        try {
            return realKeySet().size();
        }catch (Exception e) {
            return -1;
        }
    }

    @Override
    public boolean isEmpty() {
        return size() <= 0;
    }

    @Override
    public boolean containsKey(Object key) {
        try {
            Boolean ret = redisTemplate.hasKey(getRedisKey(key));
            return ret != null && ret;
        }
        catch (Exception e) {
            return false;
        }
    }

    @Override
    public boolean containsValue(Object value) {
        throw new BizException("未实现此方法");
    }

    @Override
    public V get(Object key) {
        try {
            return redisTemplate.opsForValue().get(getRedisKey(key));
        }
        catch (Exception e) {
            return null;
        }
    }

    @Override
    public V put(String key, V value) {
        try {
            if (timeOut > 0) {
                redisTemplate.opsForValue().set(getRedisKey(key), value, Duration.ofMillis(timeOut));
            }
            else {
                redisTemplate.opsForValue().set(getRedisKey(key), value);
            }
            return value;
        }
        catch (Exception e) {
            return null;
        }
    }

    @Override
    public V remove(Object key) {
        try {
            V v = redisTemplate.opsForValue().get(getRedisKey(key));
            if (v != null) {
                redisTemplate.delete(getRedisKey(key));
                return v;
            }
            return null;
        }
        catch (Exception e) {
            return null;
        }
    }

    @Override
    public void putAll(Map<? extends String, ? extends V> m) {
        try {
            if (timeOut <= 0) {
                Map<String, V> m2 = new HashMap<>();
                m.forEach((BiConsumer<String, V>) (s, v) -> m2.put(getRedisKey(s), v));
                redisTemplate.opsForValue().multiSet(m2);
            } else {
                m.forEach((BiConsumer<String, V>) this::put);
            }
        }
        catch (Exception ignored) {
        }
    }

    @Override
    public void clear() {
        try {
            Set<String> keys = redisTemplate.keys(mapName.concat("*"));
            if (keys != null) {
                redisTemplate.delete(keys);
            }
        } catch (Exception ignored) {
        }
    }

    @Override
    public Set<String> keySet() {
        try {
            Set<String> keys = realKeySet();
            Set<String> retKeys = new HashSet<>();
            keys.forEach(s -> retKeys.add(s.substring(mapName.length())));
            return retKeys;
        } catch (Exception ignored) {
            return new HashSet<>(0);
        }
    }

    private Set<String> realKeySet() {
        try {
            Set<String> keys = redisTemplate.keys(mapName.concat("*"));
            if (keys == null) {
                return new HashSet<>(0);
            }
            return keys;
        } catch (Exception ignored) {
            return new HashSet<>(0);
        }
    }

    @Override
    public Collection<V> values() {
        try {
            Set<String> keys = realKeySet();
            if (keys.isEmpty()) {
                return new ArrayList<>(0);
            }
            List<V> values = redisTemplate.opsForValue().multiGet(keys);
            if (values == null) {
                return new ArrayList<>(0);
            }
            return values;
        } catch (Exception ignored) {
            return new ArrayList<>(0);
        }
    }

    @Override
    public Set<Entry<String, V>> entrySet() {
        try {
            Set<String> keys = realKeySet();
            if (keys.isEmpty()) {
                return new HashSet<>(0);
            }
            List<String> keyList = new ArrayList<>(keys);
            List<V> values = redisTemplate.opsForValue().multiGet(keyList);
            if (values == null) {
                return new HashSet<>(0);
            }
            Set<Entry<String, V>> entries = new HashSet<>();
            for (int i = 0; i < values.size(); i++) {
                if (values.get(i) == null)
                    continue;
                entries.add(new AbstractMap.SimpleEntry<>(keyList.get(i).substring(mapName.length()), values.get(i)));
            }
            return entries;
        } catch (Exception ignored) {
            return new HashSet<>(0);
        }
    }
}
